import tkinter as tk  # tkinter是Python内置的GUI库,用于创建图形用户界面
from tkinter import filedialog  # filedialog是tkinter中的一个模块,用于显示文件对话框
from tkinter import messagebox  # messagebox是tkinter中的一个模块,用于显示消息框
from PIL import Image, ImageTk, ImageEnhance  # PIL是Python的图像处理库,用于加载、操作和保存图像
import os  # os模块提供了与操作系统交互的功能,如文件路径操作
import numpy as np  # numpy是Python的数值计算库,用于处理数组和矩阵
import image_channels  # image_channels是一个自定义模块,用于分割和合并图像通道
import image_io  # image_io是一个自定义模块,用于加载和保存图像
import image_transform  # image_transform是一个自定义模块,用于图像变换
import config  # config是一个自定义模块,用于存储应用程序的配置信息
import sys  # sys模块提供了与Python解释器和运行环境相关的变量和函数
from image_utils import language_data, logging  # 从image_utils模块中导入language_data和logging
import image_utils as utils  # 导入image_utils模块并给它一个别名utils
import subprocess  # subprocess模块允许你生成新的进程，连接它们的输入、输出和错误管道，并获取它们的返回码


class ImageProcessor:
    def __init__(self, master):
        """初始化ImageProcessor类的实例"""
        self.master = master  # 主窗口
        self.current_language = language_data["current_language"]  # 默认语言通过JSON文件直接设置
        master.title(language_data[self.current_language]['app_title'])  # 设置初始窗口标题

        self.image = None  # 用于存储当前图像的numpy数组
        self.image_path = ""  # 用于存储当前图像的文件路径
        self.rect_start_x = None  # 用于存储裁剪矩形框起始点的x坐标
        self.rect_start_y = None  # 用于存储裁剪矩形框起始点的y坐标
        self.rect_end_x = None  # 用于存储裁剪矩形框结束点的x坐标
        self.rect_end_y = None  # 用于存储裁剪矩形框结束点的y坐标
        self.is_drawing = False  # 用于标记是否正在绘制裁剪矩形框
        self.current_rect = None  # 用于存储当前绘制的裁剪矩形框对象
        self.original_image = None  # 用于存储原始图像，以便重置
        self.brightness_factor = 1.0  # 亮度调节因子，默认为1.0(即原始亮度)
        self.brightness_window = None  # 亮度调节窗口
        self.threshold_window = None  # 阈值调节窗口
        self.threshold_value = 128  # 初始阈值设置为128
        self.edge_window = None  # 边缘检测窗口
        self.shift_value = 0  # 初始位移值
        self.mask_image = None  # 存储遮罩图像

        self.create_widgets()  # 创建GUI组件

    def create_widgets(self):
        """创建GUI组件"""
        # 创建菜单栏
        menubar = tk.Menu(self.master)
        self.master.config(menu=menubar)

        # 创建文件菜单
        file_menu = tk.Menu(menubar)
        file_menu.add_command(label=language_data[self.current_language]['open_original'],
                              command=self.Open_as_original)
        file_menu.add_command(label=language_data[self.current_language]['open_grayscale'], command=self.Open_as_gray)
        file_menu.add_command(label=language_data[self.current_language]['merge_channels'], command=self.merge_channels)
        file_menu.add_command(label=language_data[self.current_language]['save_image'], command=self.save_image)
        file_menu.add_separator()

        # 创建语言菜单
        language_menu = tk.Menu(file_menu)
        self.language_var = tk.StringVar(value=self.current_language)
        language_menu.add_radiobutton(label="中文", variable=self.language_var, value="zh",
                                      command=self.change_language)
        language_menu.add_radiobutton(label="English", variable=self.language_var, value="en",
                                      command=self.change_language)
        file_menu.add_cascade(label=language_data[self.current_language]['language'], menu=language_menu)

        file_menu.add_command(label=language_data[self.current_language]['exit'], command=self.master.quit)
        menubar.add_cascade(label=language_data[self.current_language]['file'], menu=file_menu)

        # 创建信息菜单
        info_menu = tk.Menu(menubar)
        info_menu.add_command(label=language_data[self.current_language]['details'], command=self.show_image_details)
        menubar.add_cascade(label=language_data[self.current_language]['info'], menu=info_menu)

        # 创建编辑菜单
        edit_menu = tk.Menu(menubar)
        edit_menu.add_command(label=language_data[self.current_language]['crop'], command=self.crop_image)
        edit_menu.add_command(label=language_data[self.current_language]['reset'], command=self.reset_image)
        edit_menu.add_command(label=language_data[self.current_language]['brightness'], command=self.adjust_brightness)
        menubar.add_cascade(label=language_data[self.current_language]['edit'], menu=edit_menu)

        # 创建图像分割菜单
        split_menu = tk.Menu(menubar)
        menubar.add_cascade(label=language_data[self.current_language]['split'], menu=split_menu)
        split_menu.add_command(label=language_data[self.current_language]['split_channels'],
                               command=self.split_image_channels)

        # 创建阈值处理菜单
        threshold_menu = tk.Menu(menubar)
        threshold_menu.add_command(label=language_data[self.current_language]['global_threshold'],
                                   command=self.adjust_threshold)
        menubar.add_cascade(label=language_data[self.current_language]['threshold_processing'], menu=threshold_menu)

        # 创建边缘检测菜单
        edge_menu = tk.Menu(menubar)
        edge_menu.add_command(label=language_data.get(self.current_language, {}).get('edge_detection', '边缘检测'),
                              command=self.edge_detection)
        edge_menu.add_command(label=language_data.get(self.current_language, {}).get('shift_difference', '移动差分'),
                              command=self.shift_difference)
        menubar.add_cascade(label=language_data.get(self.current_language, {}).get('edge', '边缘'), menu=edge_menu)

        # 创建掩模菜单
        mask_menu = tk.Menu(menubar)
        menubar.add_cascade(label=language_data.get(self.current_language, {}).get('mask', '掩模'), menu=mask_menu)
        mask_menu.add_command(label=language_data.get(self.current_language, {}).get('red_mask', '红色掩模'),
                              command=lambda: self.create_mask('red'))
        mask_menu.add_command(label=language_data.get(self.current_language, {}).get('green_mask', '绿色掩模'),
                              command=lambda: self.create_mask('green'))
        mask_menu.add_command(label=language_data.get(self.current_language, {}).get('blue_mask', '蓝色掩模'),
                              command=lambda: self.create_mask('blue'))

        # 创建位运算菜单
        bitwise_menu = tk.Menu(menubar)
        bitwise_menu.add_command(label=language_data.get(self.current_language, {}).get('bitwise_and', '与运算（遮罩）'),
                                 command=self.bitwise_and)
        bitwise_menu.add_command(label=language_data.get(self.current_language, {}).get('bitwise_or', '或运算（融合）'),
                                 command=self.bitwise_or)
        bitwise_menu.add_command(
            label=language_data.get(self.current_language, {}).get('bitwise_xor', '异或运算（差异）'),
            command=self.bitwise_xor)
        bitwise_menu.add_command(label=language_data.get(self.current_language, {}).get('bitwise_not', '非运算（反转）'),
                                 command=self.bitwise_not)
        menubar.add_cascade(label=language_data.get(self.current_language, {}).get('bitwise_operations', '位运算'),
                            menu=bitwise_menu)

        # 创建图像显示区域
        self.canvas = tk.Canvas(self.master, width=600, height=400)
        self.canvas.pack()

        self.canvas.bind("<Button-1>", self.on_mouse_down)
        self.canvas.bind("<B1-Motion>", self.on_mouse_move)
        self.canvas.bind("<ButtonRelease-1>", self.on_mouse_up)

    def change_language(self):
        """切换语言"""
        # 把language.json文件中的current_language的值改为当前的self.current_language
        self.current_language = self.language_var.get()  # 获取所选择的语言
        utils.write_language_data(language_data, "current_language", self.current_language)
        logging.info(f"当前语言已切换为{self.current_language}。")
        confirm = messagebox.askyesno(language_data[self.current_language]['confirm_restart'],
                                      language_data[self.current_language]['restart_warning'])
        if confirm:
            if self.brightness_window:
                self.brightness_window.destroy()
            if self.threshold_window:
                self.threshold_window.destroy()
            if self.edge_window:
                self.edge_window.destroy()
            self.master.destroy()
            # os.execl(sys.executable, sys.executable, *sys.argv)
            subprocess.Popen([sys.executable] + sys.argv)

    def merge_channels(self):
        """合并多个单通道图像为一个多通道图像"""
        # 打开文件对话框,让用户选择多个图像文件
        file_paths = filedialog.askopenfilenames()
        # 如果用户没有选择文件,直接返回
        if not file_paths:
            return

        # 如果只选择了一个文件,显示错误信息并返回
        if len(file_paths) == 1:
            tk.messagebox.showerror(language_data[self.current_language]['error'],
                                    language_data[self.current_language]['single_file_error'])
            return

        try:
            self.image = image_channels.merge_image_channels(file_paths)
        except ValueError as e:
            tk.messagebox.showerror(language_data[self.current_language]['error'], str(e))
            return

        # 显示当前图像
        self.display_image()

    def split_image_channels(self):
        """分割当前图像的通道,并在新窗口中显示每个通道"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        if len(self.image.shape) == 2:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['single_channel_error'])
            return

        try:
            channels = image_channels.split_image_channels(self.image)
        except ValueError as e:
            tk.messagebox.showinfo(language_data[self.current_language]['error'], str(e))
            return

        for i, channel in enumerate(channels):
            window = tk.Toplevel(self.master)
            window.title(language_data[self.current_language]['channel_title'].format(i))
            canvas = tk.Canvas(window, width=300, height=300)
            canvas.pack()

            # 定义内部函数用于保存单个通道
            def save_channel(channel, index):
                save_path = filedialog.asksaveasfilename(defaultextension=config.DEFAULT_SAVE_FORMAT)
                if not save_path:
                    return
                image_io.save_image(channel, save_path)
                tk.messagebox.showinfo(language_data[self.current_language]['success'],
                                       language_data[self.current_language]['channel_saved'].format(index, save_path))

            save_button = tk.Button(window, text=language_data[self.current_language]['save_channel'],
                                    command=lambda channel=channel: save_channel(channel, i))
            save_button.pack()

            window.update()

            # 显示子通道图像
            image = Image.fromarray(channel)  # 将通道数据转换为PIL图像对象
            canvas_width = canvas.winfo_width()  # 获取画布的宽度
            canvas_height = canvas.winfo_height()  # 获取画布的高度
            image_width, image_height = image.size  # 获取图像的宽度和高度
            scale = min(canvas_width / image_width, canvas_height / image_height)  # 计算图像的缩放比例
            if scale < 1:  # 如果图像过大,缩小图像
                new_width = int(image_width * scale)
                new_height = int(image_height * scale)
                image = image.resize((new_width, new_height), Image.LANCZOS)  # 使用Lanczos滤波器进行缩小
            else:  # 如果图像过小,放大图像
                new_width = int(image_width * scale)
                new_height = int(image_height * scale)
                image = image.resize((new_width, new_height), Image.BICUBIC)  # 使用双三次滤波器进行放大

            photo = ImageTk.PhotoImage(image)  # 将PIL图像对象转换为PhotoImage对象
            x = (canvas_width - new_width) // 2  # 计算图像在画布上的水平居中位置
            y = (canvas_height - new_height) // 2  # 计算图像在画布上的垂直居中位置
            canvas.create_image(x, y, anchor=tk.NW, image=photo)  # 在画布上显示图像
            canvas.image = photo  # 将PhotoImage对象保存到画布中,防止被垃圾回收

            # 在子图上添加清晰的标题
            title = f"Channel {i}"
            canvas.create_text(canvas_width // 2, 15, text=title, font=("Arial", 15), fill="black")

        logging.info("图像分割操作成功。")  # 记录成功日志

    def create_mask(self, channel):
        """创建并显示指定颜色通道的掩模"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        # 检查是否为彩色图像
        if len(self.image.shape) != 3 or self.image.shape[2] != 3:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data.get(self.current_language, {}).get('color_image_required',
                                                                                    '需要彩色图像才能创建掩模'))
            return

        # 创建掩模
        mask = np.zeros_like(self.image)

        if channel == 'red':
            mask[:, :, 0] = self.image[:, :, 0]  # 保留红色通道
            title = language_data.get(self.current_language, {}).get('red_mask', '红色掩模')
        elif channel == 'green':
            mask[:, :, 1] = self.image[:, :, 1]  # 保留绿色通道
            title = language_data.get(self.current_language, {}).get('green_mask', '绿色掩模')
        elif channel == 'blue':
            mask[:, :, 2] = self.image[:, :, 2]  # 保留蓝色通道
            title = language_data.get(self.current_language, {}).get('blue_mask', '蓝色掩模')
        else:
            return

        # 保存掩模供后续位运算使用
        self.mask_image = mask

        # 在新窗口中显示掩模
        window = tk.Toplevel(self.master)
        window.title(title)
        canvas = tk.Canvas(window, width=600, height=400)
        canvas.pack()

        # 保存掩模的函数
        def save_mask():
            save_path = filedialog.asksaveasfilename(defaultextension=config.DEFAULT_SAVE_FORMAT)
            if not save_path:
                return
            image_io.save_image(mask, save_path)
            tk.messagebox.showinfo(language_data[self.current_language]['success'],
                                   language_data.get(self.current_language, {}).get('mask_saved', '掩模已保存'))
            logging.info(f"{title}已保存到 {save_path}")

        # 创建保存按钮
        save_button = tk.Button(window, text=language_data.get(self.current_language, {}).get('save_mask', 'Save Mask'),
                                command=save_mask)
        save_button.pack(pady=10)

        # 显示掩模图像
        window.update()

        # 计算图像的缩放比例
        pil_mask = Image.fromarray(mask)
        canvas_width = canvas.winfo_width()
        canvas_height = canvas.winfo_height()
        image_width, image_height = pil_mask.size
        scale = min(canvas_width / image_width, canvas_height / image_height)

        # 缩放图像
        if scale < 1:
            pil_mask = pil_mask.resize((int(image_width * scale), int(image_height * scale)), Image.LANCZOS)
        else:
            pil_mask = pil_mask.resize((int(image_width * scale), int(image_height * scale)), Image.BICUBIC)

        photo = ImageTk.PhotoImage(pil_mask)
        x = (canvas_width - pil_mask.width) // 2
        y = (canvas_height - pil_mask.height) // 2
        canvas.create_image(x, y, anchor=tk.NW, image=photo)
        canvas.image = photo

        logging.info(f"{title}已创建并显示")

    def show_image_details(self):
        """显示当前图像的详细信息"""
        if self.image is None:  # 如果当前没有图像,显示相应消息并返回
            messagebox.showinfo(language_data[self.current_language]['error'],
                                language_data[self.current_language]['no_image_loaded'])
            logging.info("没有载入任何图像,无法显示图像细节。")  # 记录日志信息
            return

        # 从路径中提取文件名
        filename = os.path.basename(self.image_path)

        # 计算其他基本信息
        total_pixels = np.prod(self.image.shape)  # 获取图像的总像素数
        shape = self.image.shape  # 获取图像的形状(高度,宽度,通道数)
        channels = self.image.shape[2] if len(self.image.shape) == 3 else 1  # 获取图像的通道数，注意不是维度，而是通道数
        dtype = self.image.dtype  # 获取图像像素的数据类型

        # 构建信息字符串,包括文件名
        details = language_data[self.current_language]['info_str'].format(filename, total_pixels, shape, channels,
                                                                          dtype)

        # 显示信息
        messagebox.showinfo("INFO", details)
        logging.info("已显示图像细节。")  # 记录成功日志

    def on_mouse_down(self, event):
        """处理鼠标左键按下事件"""
        if self.current_rect and not self.is_drawing:
            # 如果存在矩形框且不是在拖动状态,删除矩形框并重置状态
            self.canvas.delete(self.current_rect)
            self.current_rect = None
        else:
            # 否则,开始绘制新的矩形框
            self.rect_start_x = event.x
            self.rect_start_y = event.y
            self.is_drawing = True

    def on_mouse_move(self, event):
        """处理鼠标左键移动事件"""
        if self.is_drawing:
            if self.current_rect:
                self.canvas.delete(self.current_rect)
            self.current_rect = self.canvas.create_rectangle(self.rect_start_x, self.rect_start_y, event.x, event.y,
                                                             outline="red")

    def on_mouse_up(self, event):
        """处理鼠标左键释放事件"""
        # 标记矩形的结束点并完成绘制
        self.rect_end_x, self.rect_end_y = event.x, event.y
        self.is_drawing = False
        # 保存裁剪区域的坐标
        self.crop_region = (min(self.rect_start_y, self.rect_end_y), max(self.rect_start_y, self.rect_end_y),
                            min(self.rect_start_x, self.rect_end_x), max(self.rect_start_x, self.rect_end_x))
        logging.info("已绘制裁剪区域。")  # 记录成功日志

    def Open_as_original(self):
        """打开原始图像"""
        logging.info("正在打开原始图像...")
        self.image_path = filedialog.askopenfilename()  # 打开文件对话框,让用户选择图像文件
        if not self.image_path:
            logging.info("没有选择任何图像文件,打开操作已取消。")  # 记录日志信息
            return  # 如果没有选择任何文件,直接返回
        self.image = image_io.load_image(self.image_path)  # 加载图像文件
        self.original_image = self.image.copy()  # 保存原始图像
        self.display_image()  # 显示图像
        logging.info("成功打开原始图像。")  # 记录成功日志

    def Open_as_gray(self):
        """将原始图像转换为灰度图像"""
        self.image_path = filedialog.askopenfilename()  # 打开文件对话框,让用户选择图像文件
        if not self.image_path:
            logging.info("没有选择任何图像文件,打开操作已取消。")  # 记录日志信息
            return  # 如果没有选择任何文件,直接返回
        self.image = image_io.load_image(self.image_path, as_gray=True)  # 加载图像文件并转换为灰度图像
        self.original_image = self.image.copy()  # 保存原始图像
        self.display_image()  # 显示图像
        logging.info("成功打开并转换为灰度图像。")  # 记录成功日志

    def display_image(self):
        """在主画布上显示当前图像"""
        if self.image is None:
            logging.info("当前没有图像可显示。")  # 记录日志信息
            return  # 如果当前没有图像,直接返回

        image = Image.fromarray(self.image)  # 将numpy数组转换为PIL图像对象

        # 计算图像的缩放比例
        canvas_width = self.canvas.winfo_width()  # 获取画布的宽度
        canvas_height = self.canvas.winfo_height()  # 获取画布的高度
        image_width, image_height = image.size  # 获取图像的宽度和高度
        scale = min(canvas_width / image_width, canvas_height / image_height)  # 计算图像的缩放比例

        # 缩放图像
        if scale < 1:
            # 图像过大,缩小图像
            new_width = int(image_width * scale)
            new_height = int(image_height * scale)
            image = image.resize((new_width, new_height), Image.LANCZOS)  # 使用Lanczos滤波器进行缩小
        else:
            # 图像过小,放大图像
            new_width = int(image_width * scale)
            new_height = int(image_height * scale)
            image = image.resize((new_width, new_height), Image.BICUBIC)  # 使用双三次滤波器进行放大

        photo = ImageTk.PhotoImage(image)  # 将PIL图像对象转换为PhotoImage对象
        self.canvas.delete("all")  # 清除画布上的所有内容

        # 计算图像在画布上的居中位置
        x = (canvas_width - new_width) // 2
        y = (canvas_height - new_height) // 2
        self.canvas.create_image(x, y, anchor=tk.NW, image=photo)  # 在画布上显示图像, tk.NW表示图像的左上角对齐画布的左上角
        self.canvas.image = photo  # 将PhotoImage对象保存到画布中,防止被垃圾回收
        logging.info("成功显示图像。")  # 记录成功日志

    def save_image(self):
        """保存当前图像"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_to_be_saved'])
            logging.error("没有图像可保存,保存操作已取消。")  # 记录错误日志
            return  # 如果当前没有图像,直接返回

        save_path = filedialog.asksaveasfilename(defaultextension=config.DEFAULT_SAVE_FORMAT)  # 打开保存文件对话框
        if not save_path:
            logging.info("没有选择保存路径,保存操作已取消。")  # 记录日志信息
            return  # 如果没有选择保存路径,直接返回

        image_io.save_image(self.image, save_path)  # 将当前图像保存到指定路径
        logging.info(f"图像已成功保存到 {save_path}。")  # 记录成功日志

    def crop_image(self):
        """裁剪当前图像"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_to_be_cropped'])
            return

        if not hasattr(self, 'crop_region'):
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_crop_region'])
            return

        y_start, y_end, x_start, x_end = self.crop_region
        self.image = self.image[y_start:y_end, x_start:x_end]
        self.display_image()
        logging.info("图像裁剪操作成功。")

    def reset_image(self):
        """重置图像到原始状态"""
        if self.original_image is not None:
            self.image = self.original_image.copy()
            self.brightness_factor = 1.0  # 重置亮度因子
            self.threshold_value = 128  # 重置阈值
            self.shift_value = 0  # 重置位移值
            self.display_image()
            logging.info("图像已重置到原始状态。")
        else:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_original_image'])

    def adjust_brightness(self):
        """调整图像亮度"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        # 如果亮度调节窗口已存在,则销毁它
        if self.brightness_window and self.brightness_window.winfo_exists():
            self.brightness_window.destroy()

        # 创建亮度调节窗口
        self.brightness_window = tk.Toplevel(self.master)
        self.brightness_window.title(language_data[self.current_language]['brightness_adjustment'])
        self.brightness_window.geometry("300x150")
        self.brightness_window.transient(self.master)  # 设置为主窗口的子窗口
        self.brightness_window.grab_set()  # 模态窗口,阻止用户与主窗口交互

        # 创建亮度标签
        brightness_label = tk.Label(self.brightness_window,
                                    text=language_data[self.current_language]['brightness_level'])
        brightness_label.pack(pady=10)

        # 创建亮度滑块
        brightness_scale = tk.Scale(self.brightness_window, from_=0, to=200, orient=tk.HORIZONTAL,
                                    length=250, resolution=10, command=self.update_brightness)
        brightness_scale.set(int(self.brightness_factor * 100))  # 设置当前亮度值
        brightness_scale.pack(pady=10)

        # 创建应用按钮
        apply_button = tk.Button(self.brightness_window, text=language_data[self.current_language]['apply'],
                                 command=self.apply_brightness)
        apply_button.pack(pady=10)

        # 存储原始图像的副本,用于预览
        self.brightness_temp_image = self.image.copy()

    def update_brightness(self, value):
        """更新图像亮度预览"""
        # 将滑块值转换为亮度因子(0-200% -> 0.0-2.0)
        self.brightness_factor = float(value) / 100.0

        # 使用PIL的ImageEnhance调整亮度
        pil_image = Image.fromarray(self.brightness_temp_image)
        enhancer = ImageEnhance.Brightness(pil_image)
        enhanced_image = enhancer.enhance(self.brightness_factor)
        self.image = np.array(enhanced_image)

        # 更新显示
        self.display_image()

    def apply_brightness(self):
        """应用亮度调整"""
        # 更新原始图像
        self.original_image = self.image.copy()

        # 关闭亮度调节窗口
        if self.brightness_window:
            self.brightness_window.destroy()

        logging.info(f"图像亮度已调整为{int(self.brightness_factor * 100)}%")

    def adjust_threshold(self):
        """调整全局阈值"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        # 检查是否为灰度图像
        if len(self.image.shape) == 3 and self.image.shape[2] == 3:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['threshold_requires_gray'])
            return

        # 如果阈值调节窗口已存在,则销毁它
        if self.threshold_window and self.threshold_window.winfo_exists():
            self.threshold_window.destroy()

        # 创建阈值调节窗口
        self.threshold_window = tk.Toplevel(self.master)
        self.threshold_window.title(language_data[self.current_language]['global_threshold'])
        self.threshold_window.geometry("300x150")
        self.threshold_window.transient(self.master)  # 设置为主窗口的子窗口
        self.threshold_window.grab_set()  # 模态窗口,阻止用户与主窗口交互

        # 创建阈值标签
        threshold_label = tk.Label(self.threshold_window, text=language_data[self.current_language]['threshold_value'])
        threshold_label.pack(pady=10)

        # 创建阈值滑块 (0-255)
        threshold_scale = tk.Scale(self.threshold_window, from_=0, to=255, orient=tk.HORIZONTAL,
                                   length=250, resolution=1, command=self.update_threshold)
        threshold_scale.set(self.threshold_value)  # 设置当前阈值
        threshold_scale.pack(pady=10)

        # 创建应用按钮
        apply_button = tk.Button(self.threshold_window, text=language_data[self.current_language]['apply'],
                                 command=self.apply_threshold)
        apply_button.pack(pady=10)

        # 存储原始图像的副本,用于预览
        self.threshold_temp_image = self.image.copy()

        # 立即应用当前阈值进行预览
        self.update_threshold(self.threshold_value)

    def update_threshold(self, value):
        """更新阈值预览"""
        # 将滑块值转换为阈值
        self.threshold_value = int(value)

        # 应用全局阈值
        thresholded_image = np.zeros_like(self.threshold_temp_image)
        thresholded_image[self.threshold_temp_image > self.threshold_value] = 255

        # 更新显示
        self.image = thresholded_image
        self.display_image()

    def apply_threshold(self):
        """应用阈值处理"""
        # 更新原始图像
        self.original_image = self.image.copy()

        # 关闭阈值调节窗口
        if self.threshold_window:
            self.threshold_window.destroy()

        logging.info(f"图像已应用全局阈值处理，阈值为{self.threshold_value}")

    def edge_detection(self):
        """边缘检测功能"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        # 检查是否为灰度图像
        if len(self.image.shape) == 3 and self.image.shape[2] == 3:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['threshold_requires_gray'])
            return

        # 简单的边缘检测 (Sobel算子)
        sobel_x = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=np.float32)
        sobel_y = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=np.float32)

        # 计算梯度
        grad_x = image_transform.convolve(self.image, sobel_x)
        grad_y = image_transform.convolve(self.image, sobel_y)

        # 计算梯度幅值
        gradient_magnitude = np.sqrt(grad_x ** 2 + grad_y ** 2)

        # 归一化到0-255范围
        gradient_magnitude = (gradient_magnitude / np.max(gradient_magnitude) * 255).astype(np.uint8)

        # 显示边缘图像
        self.image = gradient_magnitude
        self.display_image()

        logging.info("边缘检测成功。")

    def shift_difference(self):
        """移动差分边缘检测"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        # 检查是否为灰度图像
        if len(self.image.shape) == 3 and self.image.shape[2] == 3:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['threshold_requires_gray'])
            return

        # 如果边缘检测窗口已存在,则销毁它
        if self.edge_window and self.edge_window.winfo_exists():
            self.edge_window.destroy()

        # 创建边缘检测窗口
        self.edge_window = tk.Toplevel(self.master)
        self.edge_window.title(language_data.get(self.current_language, {}).get('shift_difference', '移动差分'))
        self.edge_window.geometry("350x200")
        self.edge_window.transient(self.master)  # 设置为主窗口的子窗口
        self.edge_window.grab_set()  # 模态窗口,阻止用户与主窗口交互

        # 创建位移标签
        shift_label = tk.Label(self.edge_window,
                               text=language_data.get(self.current_language, {}).get('shift_value', '位移值'))
        shift_label.pack(pady=10)

        # 创建位移滑块 (-3到3)
        shift_scale = tk.Scale(self.edge_window, from_=-3, to=3, orient=tk.HORIZONTAL,
                               length=300, resolution=1, command=self.update_shift_difference)
        shift_scale.set(self.shift_value)  # 设置当前位移值
        shift_scale.pack(pady=10)

        # 创建应用按钮
        apply_button = tk.Button(self.edge_window, text=language_data[self.current_language]['apply'],
                                 command=self.apply_shift_difference)
        apply_button.pack(pady=10)

        # 存储原始图像的副本,用于预览
        self.shift_temp_image = self.image.copy()

        # 立即应用当前位移进行预览
        self.update_shift_difference(self.shift_value)

    def update_shift_difference(self, value):
        """更新位移差分预览"""
        # 将滑块值转换为位移值
        self.shift_value = int(value)

        # 创建位移后的图像
        shifted_image = np.zeros_like(self.shift_temp_image)

        if self.shift_value > 0:
            # 右移
            shifted_image[:, self.shift_value:] = self.shift_temp_image[:, :-self.shift_value]
        elif self.shift_value < 0:
            # 左移
            shifted_image[:, :self.shift_value] = self.shift_temp_image[:, -self.shift_value:]
        else:
            # 位移为0，使用原图
            shifted_image = self.shift_temp_image.copy()

        # 计算差分
        difference = np.abs(self.shift_temp_image.astype(np.int32) - shifted_image.astype(np.int32))

        # 归一化到0-255范围
        if np.max(difference) > 0:
            difference = (difference / np.max(difference) * 255).astype(np.uint8)
        else:
            difference = np.zeros_like(difference)

        # 更新显示
        self.image = difference
        self.display_image()

    def apply_shift_difference(self):
        """应用位移差分边缘检测"""
        # 更新原始图像
        self.original_image = self.image.copy()

        # 关闭边缘检测窗口
        if self.edge_window:
            self.edge_window.destroy()

        logging.info(f"图像已应用位移差分边缘检测，位移值为{self.shift_value}")

    def bitwise_and(self):
        """位与运算（遮罩）"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        if self.mask_image is None:
            # 如果没有预先创建的掩模，提示用户创建
            result = messagebox.askyesno(
                language_data[self.current_language]['confirm'],
                language_data.get(self.current_language, {}).get('no_mask_warning', '没有可用的掩模。是否创建红色掩模？')
            )
            if result:
                self.create_mask('red')
            return

        # 确保图像和掩模尺寸兼容
        if self.image.shape != self.mask_image.shape:
            # 尝试调整掩模大小以匹配图像
            try:
                mask_pil = Image.fromarray(self.mask_image)
                mask_pil = mask_pil.resize((self.image.shape[1], self.image.shape[0]), Image.LANCZOS)
                self.mask_image = np.array(mask_pil)
            except Exception as e:
                tk.messagebox.showerror(
                    language_data[self.current_language]['error'],
                    language_data.get(self.current_language, {}).get('mask_size_error',
                                                                     '掩模尺寸与图像不兼容') + f": {str(e)}"
                )
                return

        # 执行位与运算
        self.image = np.bitwise_and(self.image, self.mask_image)
        self.display_image()
        logging.info("位与运算（遮罩）成功")

    def bitwise_or(self):
        """位或运算（融合）"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        if self.mask_image is None:
            tk.messagebox.showinfo(
                language_data[self.current_language]['error'],
                language_data.get(self.current_language, {}).get('no_mask_error', '请先创建或加载掩模图像')
            )
            return

        # 确保图像和掩模尺寸兼容
        if self.image.shape != self.mask_image.shape:
            tk.messagebox.showerror(
                language_data[self.current_language]['error'],
                language_data.get(self.current_language, {}).get('mask_size_error', '掩模尺寸与图像不兼容')
            )
            return

        # 执行位或运算
        self.image = np.bitwise_or(self.image, self.mask_image)
        self.display_image()
        logging.info("位或运算（融合）成功")

    def bitwise_xor(self):
        """位异或运算（差异）"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        if self.mask_image is None:
            tk.messagebox.showinfo(
                language_data[self.current_language]['error'],
                language_data.get(self.current_language, {}).get('no_mask_error', '请先创建或加载掩模图像')
            )
            return

        # 确保图像和掩模尺寸兼容
        if self.image.shape != self.mask_image.shape:
            tk.messagebox.showerror(
                language_data[self.current_language]['error'],
                language_data.get(self.current_language, {}).get('mask_size_error', '掩模尺寸与图像不兼容')
            )
            return

        # 执行位异或运算
        self.image = np.bitwise_xor(self.image, self.mask_image)
        self.display_image()
        logging.info("位异或运算（差异）成功")

    def bitwise_not(self):
        """位非运算（反转）"""
        if self.image is None:
            tk.messagebox.showinfo(language_data[self.current_language]['error'],
                                   language_data[self.current_language]['no_image_loaded'])
            return

        # 执行位非运算
        self.image = np.invert(self.image)
        self.display_image()
        logging.info("位非运算（反转）成功")


if __name__ == "__main__":
    root = tk.Tk()
    app = ImageProcessor(root)
    root.mainloop()    